using System;
using System.ComponentModel;
using System.IO;
using System.Windows.Forms;
using ExtractItAll.Core;
using ExtractItAll.Extractors;

namespace ExtractItAll.Forms
{
  public partial class FileManager : UserControl
  {
    private ProgressChangedDelegate _ArchiveProgressChanged;
    private ProgressChangedDelegate _FileProgressChanged;
    private TasksChangedDelegate _TasksChanged;
    private TaskUpdatedDelegate _TaskUpdated;
    private ProgressChangedDelegate _TotalProgressChanged;

    #region Contructor

    public FileManager()
    {
      InitializeComponent();

      _TasksChanged = new TasksChangedDelegate(TasksChangedMethod);
      _TaskUpdated = new TaskUpdatedDelegate(TaskUpdatedMethod);
      _FileProgressChanged = new ProgressChangedDelegate(FileProgressChangedMethod);
      _ArchiveProgressChanged = new ProgressChangedDelegate(ArchiveProgressChangedMethod);
      _TotalProgressChanged = new ProgressChangedDelegate(TotalProgressChangedMethod);

      Program.Tasks.OnTasksChanged += TasksChangedEventHandler;
      Program.Tasks.OnFileProgressChanged += FileProgressChangedHandler;
      Program.Tasks.OnArchiveProgressChanged += ArchiveProgressChangedHandler;
      Program.Tasks.OnTotalProgressChanged += TotalProgressChangedHandler;
      Program.Tasks.OnTaskUpdated += TaskUpdatedHandler;
    }

    #endregion Contructor

    #region Destructor

    ~FileManager()
    {
      Program.Tasks.OnTasksChanged -= TasksChangedEventHandler;
      Program.Tasks.OnFileProgressChanged -= FileProgressChangedHandler;
      Program.Tasks.OnArchiveProgressChanged -= ArchiveProgressChangedHandler;
      Program.Tasks.OnTotalProgressChanged -= TotalProgressChangedHandler;
      Program.Tasks.OnTaskUpdated -= TaskUpdatedHandler;
    }

    #endregion

    #region Events

    private void FileProgressChangedHandler(ProgressInformation info)
    {
      try
      {
        Invoke(_FileProgressChanged, info);
      }
      catch (InvalidOperationException) {}
    }

    private void FileProgressChangedMethod(ProgressInformation info)
    {
      if (info.Equals(new ProgressInformation()))
      {
        info.Total = Program.Passwords.Items.Count;
        info.Completed = Program.Passwords.Pointer;
        info.CurrentItem = Program.Tasks.Active.Password;
        singleFileLabel.Text = String.Format("Currently used password ({1}/{2}): {0}",
                                             info.CurrentItem,
                                             info.Completed,
                                             info.Total
          );
      }
      else
      {
        singleFileLabel.Text = String.Format("Extracting: {0} {1} / {2}",
                                             info.CurrentItem,
                                             info.Completed,
                                             info.Total);
      }
      singleFileProgressBar.Value = info.GetPercentage();
    }

    private void ArchiveProgressChangedHandler(ProgressInformation info)
    {
      try
      {
        Invoke(_ArchiveProgressChanged, info);
      }
      catch (InvalidOperationException) { }
    }

    private void ArchiveProgressChangedMethod(ProgressInformation info)
    {
      if (info.Equals(new ProgressInformation()))
        Program.Tasks.RaiseFileProgressChanged(info);

      singleArchiveLabel.Text = String.Format("Extracting: {0} {1} / {2}",
                                              info.CurrentItem,
                                              info.Completed,
                                              info.Total);
      singleArchiveProgressBar.Value = info.GetPercentage();
    }

    private void TotalProgressChangedHandler(ProgressInformation info)
    {
      Invoke(_TotalProgressChanged, info);
    }

    private void TotalProgressChangedMethod(ProgressInformation info)
    {
      //RefreshPasswords(false);

      totalLabel.Text = String.Format("Completed Archives: {0}/{1}",
                                      info.Completed,
                                      info.Total);
      progressBarTotal.Value = info.GetPercentage();
    }


    private void TasksChangedEventHandler(TaskManager sender)
    {
      Invoke(_TasksChanged, sender);
    }

    private void TasksChangedMethod(TaskManager sender)
    {
      ListTasks.VirtualListSize = Program.Tasks.Count;
      ListTasks.Refresh();
    }

    private void TaskUpdatedHandler(TaskManager sender, TaskItem task)
    {
      try
      {
        Invoke(_TaskUpdated, sender, task);
      }
      catch (InvalidOperationException) {}
    }

    private void TaskUpdatedMethod(TaskManager sender, TaskItem task)
    {
      int index = Program.Tasks.GetIndex(task);
      ListTasks.RedrawItems(index, index, false);
    }

    #endregion Events

    #region Task (File) operations

    private void AddFiles(string[] list)
    {
      foreach (string str in list)
      {
        if (File.Exists(str))
        {
          FileInfo file = new FileInfo(str);
          Add(file);
        }
        else if (Directory.Exists(str))
        {
          DirectoryInfo dir = new DirectoryInfo(str);
          Add(dir);
        }
      }
    }

    private void Add(FileInfo file)
    {
      if (!file.Exists) return;

      ArchiveItem archiveItem = new ArchiveItem(file.FullName);
      if (archiveItem.Format == ArchiveFormat.Unknown) return;
      if (archiveItem.IsSubVolume) return;

      foreach (BaseExtractor extractor in Program.ExtractorList)
      {
        if (extractor.IsArchiveFormatSupported(archiveItem.Format))
        {
          TaskItem item = new TaskItem();
          item.Archive = archiveItem;
          item.ExtracorType = extractor.GetType();

          Program.Tasks.Add(item);
          break;
        }
      }
    }

    private void Add(DirectoryInfo dir)
    {
      if (!dir.Exists) return;

      foreach (DirectoryInfo d in dir.GetDirectories())
        Add(d);

      foreach (FileInfo f in dir.GetFiles())
        Add(f);
    }

    private void Delete()
    {
      foreach (int i in ListTasks.SelectedIndices)
      {
        if (ListTasks.Items[i].Tag is Guid)
          Program.Tasks.Remove((Guid)ListTasks.Items[i].Tag);
      }
    }

    private void Reset()
    {
      foreach (int i in ListTasks.SelectedIndices)
        if (ListTasks.Items[i].Tag is Guid)
          Program.Tasks.Get((Guid)ListTasks.Items[i].Tag).Reset();

      ListTasks.Refresh();
    }

    private void OpenFocusedItemDirectory()
    {
      if (ListTasks.FocusedItem == null) return;
      if (!(ListTasks.FocusedItem.Tag is Guid)) return;

      TaskItem item = Program.Tasks.Get((Guid)ListTasks.FocusedItem.Tag);

      string argument = String.Format("/select, \"{0}\"", item.Archive.FileName);
      System.Diagnostics.Process.Start("explorer.exe", argument);
    }

    //private void ReOrder()
    //{
    //  for (int i = 0; i < mpListView1.Items.Count; ++i)
    //  {
    //    //mpListView1.Items[i].Text = (i + 1).ToString();

    //    Channel channel = (Channel)mpListView1.Items[i].Tag;
    //    if (channel.SortOrder != i)
    //    {
    //      channel.SortOrder = i;
    //      channel.Persist();
    //    }
    //  }
    //}

    #endregion Task (File) operations

    #region Controls

    private void ListTasks_DragDrop(object sender, DragEventArgs e)
    {
      // Handle FileDrop data.
      if (e.Data.GetDataPresent(DataFormats.FileDrop))
      {
        AddFiles((string[])e.Data.GetData(DataFormats.FileDrop));
      }
    }

    private void ListTasks_DragEnter(object sender, DragEventArgs e)
    {
      // If the data is a file or a bitmap, display the copy cursor.
      if (e.Data.GetDataPresent(DataFormats.FileDrop))
      {
        e.Effect = DragDropEffects.Copy;
      }
      else
      {
        e.Effect = DragDropEffects.None;
      }
    }

    private void ListTasks_ItemDrag(object sender, ItemDragEventArgs e)
    {
      //e.Item
    }

    private void ListTasks_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
    {
      TaskItem item = Program.Tasks.Get(e.ItemIndex);

      e.Item = new ListViewItem();
      e.Item.Tag = item.Id;
      e.Item.Text = item.Status.ToString();
      e.Item.SubItems.Add(item.Archive.FileName);
      e.Item.SubItems.Add(item.Password);
      switch (item.Status)
      {
        case TaskStatus.Done:
          e.Item.StateImageIndex = 0;
          break;

        case TaskStatus.Error:
          e.Item.StateImageIndex = 1;
          e.Item.Text = item.StatusMessage;
          break;

        case TaskStatus.Working:
          e.Item.StateImageIndex = 2;
          break;

        case TaskStatus.Warning:
          e.Item.StateImageIndex = 3;
          e.Item.Text = item.StatusMessage;
          break;

        case TaskStatus.Open:
          e.Item.StateImageIndex = 4;
          break;

        default:
          e.Item.StateImageIndex = -1;
          break;
      }
    }

    private void ListTasks_KeyDown(object sender, KeyEventArgs e)
    {
      if (e.KeyCode == Keys.Delete)
        Delete();
    }

    #region Open Context Menu

    private bool _cancelContextMenu = true;
    private ListViewItem _currentListItemForContextMenu;

    private void taskItemContextMenuStrip_Opening(object sender, CancelEventArgs e)
    {
      e.Cancel = _cancelContextMenu;

      _cancelContextMenu = true;
    }

    private void ListTasks_MouseClick(object sender, MouseEventArgs e)
    {
      if (e.Button != MouseButtons.Right) return;

      _currentListItemForContextMenu = ListTasks.GetItemAt(e.X, e.Y);
      _cancelContextMenu = (_currentListItemForContextMenu == null);
    }

    #endregion Open Context Menu

    #region context menu items

    private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
    {
      Delete();
    }

    private void resetToolStripMenuItem_Click(object sender, EventArgs e)
    {
      Reset();
    }

    private void openContainingDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
    {
      OpenFocusedItemDirectory();
    }

    #endregion context menu items

    #endregion Controls

    #region Nested type: ProgressChangedDelegate

    private delegate void ProgressChangedDelegate(ProgressInformation info);

    #endregion

    #region Nested type: TasksChangedDelegate

    private delegate void TasksChangedDelegate(TaskManager sender);

    #endregion

    #region Nested type: TaskUpdatedDelegate

    private delegate void TaskUpdatedDelegate(TaskManager sender, TaskItem task);

    #endregion
  }
}